namespace Cofoundry.Domain;

/// <summary>
/// Exception thrown when an entity cannot be deleted because another
/// entity has a required dependency on it that cannot be removed.
/// </summary>
public class RequiredDependencyConstaintViolationException : ValidationErrorException
{
    const string MESSAGE = "Cannot delete this entity because one or more entities have a dependency on it.";
    const string MESSAGE_TEMPLATE = "Cannot delete this {0} because {1} '{2}' has a dependency on it.";
    const string MESSAGE_TEMPLATE_WITH_COUNT = "Cannot delete this {0} because {1} '{2}' and {3} have a dependency on it.";

    /// <summary>
    /// Initializes a new instance using an error message generated by the system.
    /// </summary>
    public RequiredDependencyConstaintViolationException()
        : base(MESSAGE)
    {

    }

    /// <summary>
    /// Initializes a new instance using a specified client-friendly error message.
    /// </summary>
    /// <param name="message">Client-friendly text describing the error.</param>
    public RequiredDependencyConstaintViolationException(string message)
        : base(message)
    {

    }

    /// <summary>
    /// Initializes a new instance using a specified client-friendly error message.
    /// and an inner exception.
    /// </summary>
    /// <param name="message">Client-friendly text describing the error.</param>
    /// <param name="innerException">Inner exception collection.</param>
    public RequiredDependencyConstaintViolationException(string message, Exception innerException)
        : base(message, innerException)
    {
    }

    /// <summary>
    /// Throws an <see cref="RequiredDependencyConstaintViolationException"/> if the <paramref name="dependencies"/>
    /// collection contains one or more entities with a <see cref="EntityDependencySummary.CanDelete"/> 
    /// property value of <see langword="false"/>.
    /// </summary>
    /// <param name="definitionOfEntityBeingDeleted">
    /// The <see cref="IEntityDefinition.EntityDefinitionCode"/> of the entity attempting to be deleted.
    /// </param>
    /// <param name="dependencies">Collection of dependencies to check.</param>
    public static void ThrowIfCannotDelete(IEntityDefinition definitionOfEntityBeingDeleted, IReadOnlyCollection<EntityDependencySummary> dependencies)
    {
        ArgumentNullException.ThrowIfNull(definitionOfEntityBeingDeleted);

        var invalidDependencies = EnumerableHelper
            .Enumerate(dependencies)
            .Where(d => !d.CanDelete);

        var numInvalidDependencies = invalidDependencies.Count();
        if (numInvalidDependencies > 0)
        {
            var requiredDependency = invalidDependencies.First();
            string message;

            if (numInvalidDependencies == 1)
            {
                message = string.Format(
                    CultureInfo.CurrentCulture,
                    MESSAGE_TEMPLATE,
                    definitionOfEntityBeingDeleted.Name.ToLower(),
                    requiredDependency.Entity.EntityDefinitionName.ToLower(),
                    requiredDependency.Entity.RootEntityTitle
                    );
            }
            else
            {
                var numberOfOtherInvalidEntityies = numInvalidDependencies - 1;
                var numberText = numberOfOtherInvalidEntityies == 2 ? "one other" : numberOfOtherInvalidEntityies + " other entities";
                message = string.Format(
                    CultureInfo.CurrentCulture,
                    MESSAGE_TEMPLATE_WITH_COUNT,
                    definitionOfEntityBeingDeleted.Name.ToLower(),
                    requiredDependency.Entity.EntityDefinitionName.ToLower(),
                    requiredDependency.Entity.RootEntityTitle,
                    numberText
                    );
            }

            throw new RequiredDependencyConstaintViolationException(message);
        }
    }
}
